package com.kamistoat.meimeistore.modulessystem.controller;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kamistoat.meimeistore.apisystem.domain.SysRole;
import com.kamistoat.meimeistore.apisystem.domain.SysUser;
import com.kamistoat.meimeistore.apisystem.entity.SysMenuEntity;
import com.kamistoat.meimeistore.apisystem.entity.SysPostEntity;
import com.kamistoat.meimeistore.apisystem.entity.SysRoleEntity;
import com.kamistoat.meimeistore.apisystem.model.LoginUser;
import com.kamistoat.meimeistore.commoncore.constant.UserConstants;
import com.kamistoat.meimeistore.commoncore.domain.R;
import com.kamistoat.meimeistore.commoncore.utils.StringUtils;
import com.kamistoat.meimeistore.commoncore.utils.poi.ExcelUtil;
import com.kamistoat.meimeistore.commoncore.web.controller.BaseController;
import com.kamistoat.meimeistore.commoncore.web.domain.AjaxResult;
import com.kamistoat.meimeistore.commoncore.web.page.TableDataInfo;
import com.kamistoat.meimeistore.commondatascope.annotation.DataScope;
import com.kamistoat.meimeistore.commonlog.annotation.Log;
import com.kamistoat.meimeistore.commonlog.enums.BusinessType;
import com.kamistoat.meimeistore.commonsecurity.annotation.InnerAuth;
import com.kamistoat.meimeistore.commonsecurity.annotation.RequiresPermissions;
import com.kamistoat.meimeistore.commonsecurity.utils.SecurityUtils;
import com.kamistoat.meimeistore.commonsecurity.utils.TokenUtils;
import com.kamistoat.meimeistore.modulessystem.service.*;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import com.kamistoat.meimeistore.apisystem.entity.SysUserEntity;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;


/**
 * 用户信息表. 由于构建 LoginUser必须得从数据库查，所以该类是需要走数据库来进行鉴权的
 *
 * @author kamistoat
 * @email linjianshanke@163.com
 * @date 2022-08-09 10:34:46
 */
@RestController
@RequestMapping("/user")
public class SysUserController extends BaseController {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private SysPostService sysPostService;
    @Autowired
    private TokenUtils tokenUtils;

    /**
     * 获取用户列表
     */
    @RequiresPermissions("system:user:list")
    @GetMapping("/list")
    @DataScope
    public TableDataInfo list(SysUser user) {
        startPage();
        List<SysUserEntity> sysUserEntityList = sysUserService.selectUserList(user);
        return getDataTable(entity2Vo(sysUserEntityList));
    }

    /**
     * 导出
     */
    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
    @RequiresPermissions("system:user:export")
    @PostMapping("/export")
    @DataScope
    public void export(HttpServletResponse response, SysUser user) {
        List<SysUserEntity> sysUserEntityList = sysUserService.selectUserList(user);
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.exportExcel(response, entity2Vo(sysUserEntityList), "用户数据");
    }

    /**
     * 导入用户文件流
     *
     * @param updateSupport 如果存在相同的数据，是更新还是抛出异常
     */
    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
    @RequiresPermissions("system:user:import")
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        List<SysUser> userList = util.importExcel(file.getInputStream());
        String operName = SecurityUtils.getUserName();
        String message = sysUserService.importUser(userList, updateSupport, operName);
        return AjaxResult.success(message);
    }

    /**
     * 插入模板?
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) throws IOException {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.importTemplateExcel(response, "用户数据");
    }

    /**
     * 获取指定用户名称构建的 LoginUser。
     * 注意该接口会被 RemoteUserService远程调用，在登录时调用。
     * 只能内部调用，使用 @InnerAuth注解。
     * 另外这里也是必须走数据库，因为此时 ThreadLocal还没构建出来呢
     */
    @InnerAuth
    @GetMapping("/info/{username}")
    public R<LoginUser> infoFeign(@PathVariable("username") String username) {
        try {
            SysUserEntity sysUserEntity = sysUserService.getOne(
                    new QueryWrapper<SysUserEntity>().eq("user_name", username));

            if (StringUtils.isNull(sysUserEntity)) {
                return R.ok();
            }

            LoginUser loginUser = buildLoginUserByEntity(sysUserEntity);

            return R.ok(loginUser);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("查询接口异常");
        }
    }

    /**
     * 获取指定用户Id构建的 LoginUser。
     * 注意该接口会被 RemoteUserService远程调用，仅仅在更新用户核心令牌缓存时被调用
     * 只能内部调用，使用 @InnerAuth注解。
     * 另外这里也是必须走数据库
     */
    @InnerAuth
    @GetMapping("/info/byId/{userId}")
    public R<LoginUser> infoByUserIdFeign(@PathVariable("userId") Long userId) {
        try {
            SysUserEntity sysUserEntity = sysUserService.getOne(
                    new QueryWrapper<SysUserEntity>().eq("user_id", userId));

            if (StringUtils.isNull(sysUserEntity)) {
                return R.ok();
            }

            LoginUser loginUser = buildLoginUserByEntity(sysUserEntity);

            return R.ok(loginUser);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("查询接口异常");
        }
    }

    private LoginUser buildLoginUserByEntity(SysUserEntity sysUserEntity) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserid(sysUserEntity.getUserId());
        loginUser.setUsername(sysUserEntity.getUserName());
        loginUser.setSysUser(entity2Vo(sysUserEntity));

        // 构建角色相关内容。这里是走数据库，之后就是ThreadLocal了
        HashMap<String, Object> rolesAndRoleIds = getRolesAndRoleIds(sysUserEntity.getUserId());
        loginUser.setRoles((ArrayList<String>) rolesAndRoleIds.get("roleKeys"));
        loginUser.setRoleIds((ArrayList<Long>) rolesAndRoleIds.get("roleIds"));

        // 构建权限相关内容。同样是走数据库
        HashMap<String, Object> menusAndMenuIds = getMenusAndMenuIds(sysUserEntity.getUserId());
        loginUser.setPermissions((ArrayList<String>) menusAndMenuIds.get("menuPerms"));
        loginUser.setPermissionIds((ArrayList<Long>) menusAndMenuIds.get("menuIds"));

//        // 岗位。同样是走数据库
//        HashMap<String, Object> postAndPostIds = getPostAndPostIds(sysUserEntity.getUserId());
//        loginUser.setPosts((ArrayList<String>) postAndPostIds.get("postNames"));
//        loginUser.setPostIds((ArrayList<Long>) postAndPostIds.get("postIds"));

        if(loginUser.getRoles().contains("admin")){
            loginUser.setWeatherAdmin(true);
        }
        return loginUser;
    }

    /**
     * 注册用户信息。
     * 远程接口，将在 RemoteUserService 中被调用。
     */
    @InnerAuth
    @PostMapping("/register")
    public R<Boolean> registerFeign(@RequestBody SysUser sysUser) {
        if (!("true".equals(sysConfigService.selectConfigByKey("sys.account.registerUser")))) {
            return R.fail("当前系统没有开启注册功能！");
        }
        AjaxResult ajaxResult = add(sysUser);
        return ajaxResult.isSuccess() ? R.ok() : R.fail((String) ajaxResult.get("msg"));
    }

    /**
     * 获取当前用户的 SYSUser信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", loginUser.getSysUser());
        ajax.put("roles", loginUser.getRoles());
        ajax.put("permissions", loginUser.getPermissions());
        return ajax;
    }

    /**
     * 根据用户编号获取用户详细信息
     */
    @RequiresPermissions("system:user:query")
    @GetMapping(value = {"/", "/{userId}"})
    @DataScope
    public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
        AjaxResult ajax = AjaxResult.success();
        if (StringUtils.isNotNull(userId)) {
            SysUser sysUser = new SysUser();
            sysUser.setUserId(userId);
            SysUserEntity sysUserEntity = sysUserService.selectUserList(sysUser).get(0);


            ajax.put(AjaxResult.DATA_TAG, entity2Vo(sysUserEntity));
            // 同时放回当前用户有权限访问的角色，用于修改指定的用户的角色
//            HashMap<String, Object> postAndPostIds = getPostAndPostIds(SecurityUtils.getUserId());
//            ajax.put("posts", (ArrayList<SysPostEntity>) postAndPostIds.get("postEntitys"));
//            ajax.put("postIds", (ArrayList<Long>) postAndPostIds.get("postIds"));
            // 获取当前用户有权限访问的角色
            HashMap<String, Object> curRolesAndRoleIds = getRolesAndRoleIds(SecurityUtils.getUserId());
            // 获取指定用户已拥有的角色
            HashMap<String, Object> tarRolesAndRoleIds = getRolesAndRoleIds(userId);
            // roles:当前用户有权限访问的角色
            ajax.put("roles", (ArrayList<SysRoleEntity>) curRolesAndRoleIds.get("roleEntitys"));
            // roleIds:指定用户已经拥有的角色id
            ajax.put("roleIds", (ArrayList<Long>) tarRolesAndRoleIds.get("roleIds"));
        }
        return ajax;
    }

    /**
     * 新增用户
     */
    @RequiresPermissions("system:user:add")
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping
    @DataScope
    public AjaxResult add(@Validated @RequestBody SysUser user) {
        // 如果是注册时的add会跳过第一个if
        if (StringUtils.isNotNull(SecurityUtils.getLoginUser()) && StringUtils.isNotNull(user.getDeptId()) &&
                !SecurityUtils.getLoginUser().getAllowedDeptIds().contains(user.getDeptId())) {
            return AjaxResult.error("您没有权利向id为'" + user.getDeptId() + "'的部门中添加新用户");
        } else if (sysUserService.checkUserNameUnique(user)) {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败，该账号名称已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber())
                && sysUserService.checkPhoneUnique(user)) {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail())
                && sysUserService.checkEmailUnique(user)) {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        user.setCreateBy(SecurityUtils.getUserName());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        return toAjax(sysUserService.insertUser(user));
    }

    /**
     * 修改用户
     */
    @RequiresPermissions("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysUser user) {
        if (sysRoleService.isAdminWithUserId(user.getUserId())) {
            return AjaxResult.error("不允许修改超级管理员");
        } else if (user.getDeptId() != null && !SecurityUtils.getLoginUser().getAllowedDeptIds().contains(user.getDeptId())) {
            // 修改前和修改后的部门都应该是权限范围内
            // 修改前:查询可修改用户时只返回权限内的
            // 修改后:限制新用户的部门Id
            return AjaxResult.error("您没有权利修改id为'" + user.getDeptId() + "'的部门中的用户");
        } else if (sysUserService.checkUserNameUnique(user)) {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败，该账号名称已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber())
                && sysUserService.checkPhoneUnique(user)) {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail())
                && sysUserService.checkEmailUnique(user)) {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        user.setUpdateBy(SecurityUtils.getUserName());
        user.setUpdateTime(new Date());

        // 更新数据库
        boolean updateUser = sysUserService.updateUser(user);
        if (updateUser) {
            // 更新 后端登录缓存
            tokenUtils.updateCoreLoginUserCacheOnLine(Arrays.asList(user.getUserId()));
            return toAjax(true);
        }
        return toAjax(false);
    }

    /**
     * 删除用户
     */
    @RequiresPermissions("system:user:remove")
    @Log(title = "用户管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{userIds}")
    public AjaxResult remove(@PathVariable Long[] userIds) {
        if (ArrayUtils.contains(userIds, SecurityUtils.getUserId())) {
            return AjaxResult.error("当前用户不能删除");
        }
        List<SysUserEntity> deletedList = sysUserService.listByIds(Arrays.asList(userIds));
        List<Long> allowedDeptIds = SecurityUtils.getLoginUser().getAllowedDeptIds();
        for (SysUserEntity sysUserEntity : deletedList) {
            if (StringUtils.isNotEmpty(allowedDeptIds) && !allowedDeptIds.contains(sysUserEntity.getDeptId())) {
                return AjaxResult.error("你没有权限删除位于id为'" + sysUserEntity.getDeptId() + "'部门下的用户");
            }
        }
        return toAjax(sysUserService.removeByIds(Arrays.asList(userIds)));
    }

    /**
     * 重置密码
     */
    @RequiresPermissions("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/resetPwd")
    @DataScope
    public AjaxResult resetPwd(@RequestBody SysUser user) {
        if (!SecurityUtils.getLoginUser().getAllowedDeptIds().contains(user.getDeptId())) {
            return AjaxResult.error("您没有权利修改id为'" + user.getDeptId() + "'的部门中的用户");
        }
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        user.setUpdateBy(SecurityUtils.getUserName());
        return toAjax(sysUserService.updateUser(user));
    }

    /**
     * 状态修改
     */
    @RequiresPermissions("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    @DataScope
    public AjaxResult changeStatus(@RequestBody SysUser user) {
        if (!SecurityUtils.getLoginUser().getAllowedDeptIds().contains(user.getDeptId())) {
            return AjaxResult.error("您没有权利修改id为'" + user.getDeptId() + "'的部门中的用户");
        }
        user.setUpdateBy(SecurityUtils.getUserName());
        return toAjax(sysUserService.updateUser(user));
    }

    /**
     * 根据用户编号获取其拥有的角色
     */
    @RequiresPermissions("system:user:query")
    @GetMapping("/authRole/{userId}")
    @DataScope
    public AjaxResult authRole(@PathVariable("userId") Long userId) {
        AjaxResult ajax = AjaxResult.success();

        // 检查当前用户是否有权限访问目标用户
        SysUserEntity one = sysUserService.getById(userId);
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getAllowedDeptIds().contains(one.getDeptId())) {
            return AjaxResult.error("您没有权利获取id为'" + one.getDeptId() + "'的部门中的用户");
        }
        List<SysRoleEntity> roleList = sysRoleService.selectRoleList(new SysRole(), userId);

        ajax.put("user", entity2Vo(one));
        ajax.put("roles", roleList.stream().map(sysRoleEntity -> {
            SysRole sysRole = new SysRole();
            BeanUtils.copyProperties(sysRoleEntity, sysRole);
            return sysRole;
        }).collect(Collectors.toList()));
        return ajax;
    }

    /**
     * 为用户授权角色
     */
    @RequiresPermissions("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.GRANT)
    @PutMapping("/authRole")
    @DataScope
    public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
        SysUserEntity one = sysUserService.getById(userId);
        if (!SecurityUtils.getLoginUser().getAllowedDeptIds().contains(one.getDeptId())) {
            return AjaxResult.error("您没有权利操作id为'" + one.getDeptId() + "'的部门中的用户");
        }
        return toAjax(sysUserService.insertUserAuthRole(userId, roleIds));
    }

    /**
     * 为用户授权岗位
     */
    @RequiresPermissions("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.GRANT)
    @PutMapping("/authPost")
    @DataScope
    public AjaxResult insertAuthPost(Long userId, Long[] postIds) {
        SysUserEntity one = sysUserService.getById(userId);
        if (!SecurityUtils.getLoginUser().getAllowedDeptIds().contains(one.getDeptId())) {
            return AjaxResult.error("您没有权利操作id为'" + one.getDeptId() + "'的部门中的用户");
        }
        return toAjax(sysUserService.insertUserAuthPost(userId, postIds));
    }


    /**
     * 获取用户拥有的normal角色roleNames roleIds roleEntitys。要走数据库
     */
    @DataScope
    private HashMap<String, Object> getRolesAndRoleIds(Long userId) {
        List<SysRoleEntity> roleList = sysRoleService.selectRoleListUnderNormal(userId);
        // 超级管理员返回所有角色
        if(roleList.stream().anyMatch(sysRoleEntity -> sysRoleEntity.getRoleKey().equals("admin"))){
            roleList = sysRoleService.list(new QueryWrapper<SysRoleEntity>().eq("del_flag", UserConstants.ROLE_NORMAL));
        }
        HashMap<String, Object> map = new HashMap<>();
        Set<String> roles = new LinkedHashSet<>();
        Set<Long> roleIds = new LinkedHashSet<>();
        for (SysRoleEntity sysRoleEntity : roleList) {
            roles.add(sysRoleEntity.getRoleKey());
            roleIds.add(sysRoleEntity.getRoleId());
        }
        map.put("roleKeys", new ArrayList<>(roles));
        map.put("roleIds", new ArrayList<>(roleIds));
        map.put("roleEntitys", roleList);
        return map;
    }

    /**
     * 获取用户拥有的normal权限 menus menuIds menuList. 要走数据库
     */
    @DataScope
    private HashMap<String, Object> getMenusAndMenuIds(Long userId) {
        List<SysMenuEntity> menuList = sysMenuService.selectMenuListUnderNormal(userId);
        HashMap<String, Object> map = new HashMap<>();
        Set<String> menus = new LinkedHashSet<>();
        Set<Long> menuIds = new LinkedHashSet<>();
        for (SysMenuEntity sysMenuEntity : menuList) {
            if(StringUtils.isNotEmpty(sysMenuEntity.getPerms())){
                menus.add(sysMenuEntity.getPerms());
            }
            menuIds.add(sysMenuEntity.getMenuId());
        }
        map.put("menuPerms", new ArrayList<>(menus));
        map.put("menuIds", new ArrayList<>(menuIds));
        map.put("menuEntitys", menuList);
        return map;
    }

    /**
     * 获取用户拥有的normal岗位 posts postIds postList
     */
    @DataScope
    private HashMap<String, Object> getPostAndPostIds(Long userId) {
        List<SysPostEntity> postList = sysPostService.selectPostListUnderNormal(userId);
        HashMap<String, Object> map = new HashMap<>();
        Set<String> posts = new LinkedHashSet<>();
        Set<Long> postIds = new LinkedHashSet<>();
        for (SysPostEntity sysPostEntity : postList) {
            posts.add(sysPostEntity.getPostName());
            postIds.add(sysPostEntity.getPostId());
        }
        map.put("postNames", new ArrayList<>(posts));
        map.put("postIds", new ArrayList<>(postIds));
        map.put("postEntitys", postList);
        return map;
    }


    /**
     * Entity 和 Vo类的转换
     */
    private List<SysUser> entity2Vo(List<SysUserEntity> sysUserEntityList) {
        return
                sysUserEntityList.stream().map(sysUserEntity -> {
                    SysUser sysUser = new SysUser();
                    BeanUtils.copyProperties(sysUserEntity, sysUser);
                    return sysUser;
                }).collect(Collectors.toList());
    }

    private SysUser entity2Vo(SysUserEntity sysUserEntity) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(sysUserEntity, sysUser);
        return sysUser;
    }

    private List<SysUserEntity> vo2Entity(List<SysUser> sysUserList) {
        return
                sysUserList.stream().map(sysUser -> {
                    SysUserEntity sysUserEntity = new SysUserEntity();
                    BeanUtils.copyProperties(sysUser, sysUserEntity);
                    return sysUserEntity;
                }).collect(Collectors.toList());
    }

    private SysUserEntity vo2Entity(SysUser sysUser) {
        SysUserEntity sysUserEntity = new SysUserEntity();
        BeanUtils.copyProperties(sysUser, sysUserEntity);
        return sysUserEntity;
    }
}
